#pragma once

#include<iostream>
#include <sys/epoll.h>  
#include<unistd.h>
#include<sys/select.h>
#include <sys/types.h>         
#include <sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#include <fcntl.h>  


#include"Log.hpp"

class EpollServer
{
    const int defaultListenSocketNum=5;
    static const int defaultMaxEvent=10;
    const int epollSize=1024;
    const int timeout=-1;
public:
    EpollServer (int port)
    {
        //创建监听套接字
        _listenSocket=socket(AF_INET,SOCK_STREAM,0);
        if(_listenSocket<0)
        {
            Log(FATAL,"create listening socket error\n");
            return;
        }

        //设置地址复用
        int opt=1;
        if(setsockopt(_listenSocket,SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))<0)
        {
            Log(ERROR,"set address reuse error\n");
        }

        //绑定端口号
        struct sockaddr_in saddr;
        bzero(&saddr,sizeof(saddr));
        saddr.sin_family=AF_INET;
        saddr.sin_port=port;
        saddr.sin_addr.s_addr=INADDR_ANY;
        
        if(bind(_listenSocket,(struct sockaddr*)(&saddr),sizeof(saddr))<0)
        {
            Log(FATAL,"bind error\n");
        }

        //开启监听
        if(listen(_listenSocket,defaultListenSocketNum)<0)
        {
            Log(FATAL,"listen error\n");
        }
        
        //创建epoll对象
        _epollFd=epoll_create(epollSize);
        if(_epollFd<0)
        {
            Log(FATAL,"create epoll error\n");
            return;
        }

        //为listen套接字设置读事件监视
        struct epoll_event ee;
        bzero(&ee,sizeof(ee));
        ee.events=EPOLLIN;
        ee.data.fd=_listenSocket;
        SetNoBlocking(_listenSocket);
        if(epoll_ctl(_epollFd,EPOLL_CTL_ADD,_listenSocket,&ee)<0)
        {
            Log(FATAL,"set listenSocket monitor error\n");
            return;
        }
    }

    ~EpollServer()
    {
        if(_listenSocket>=0)
        {
            close(_listenSocket);
        }
    }
  
    void SetNoBlocking(int fd) 
    {  
        int flags = fcntl(fd, F_GETFL, 0);  
        if (flags == -1) 
        {  
            Log(ERROR,"get fd status error\n");
            return;  
        }  
        flags |= O_NONBLOCK;  
        if (fcntl(fd, F_SETFL, flags) == -1) 
        {  
            Log(ERROR,"set fd noBlocking error\n");
        }  
    }

    void GetNewClientLink()
    {
        struct sockaddr_in caddr;
        unsigned int len=sizeof(caddr);
        int sockfd=accept(_listenSocket,(struct sockaddr*)(&caddr),&len);
        if(sockfd<0)
        {
            Log(ERROR,"accept error\n");
            return;
        }

        //将新连接的文件描述符放到epoll中进行读事件监视
        struct epoll_event ee;
        bzero(&ee,sizeof(ee));
        ee.events=EPOLLIN;
        ee.data.fd=sockfd;
        SetNoBlocking(ee.data.fd);
        if(epoll_ctl(_epollFd,EPOLL_CTL_ADD,sockfd,&ee)<0)
        {
            Log(ERROR,"set read event monitor error\n");
            close(sockfd);
            return;
        }
        Log(DEBUG,"set read event monitor successfully\n");
    }

    void ServerIO(int fd)
    {
        char buf[1024];
        std::string read_buf;
        while(true)
        {
            int n=read(fd,buf,sizeof(buf));
            if(n>0)
            {
                read_buf+=buf;
            }
            else if(0==n)
            {
                Log(DEBUG,"the client close link\n");
                close(fd);
                return;
            }
            else
            {
                if(0!=read_buf.size())
                {
                    printf("read: %s\n",read_buf.c_str());
                    break;
                }
                Log(ERROR,"read fd %d error\n",fd);
                return;
            }
        }
    }


    void HandleEvent(int num)
    {
        while(num--)
        {
            if(_ready[num].data.fd==_listenSocket)
            {
                Log(DEBUG,"new client link is arrival\n");
                GetNewClientLink();
            }
            else
            {
                //在这个使用事例中我们只关心读
                Log(DEBUG,"normal IO\n");
                ServerIO(_ready[num].data.fd);
            }
        }
    }

    void Start()
    {
        while(true)
        {
            int n=epoll_wait(_epollFd,_ready,defaultMaxEvent,timeout);
            if(n>0)
            {
                Log(DEBUG,"events are ready\n");
                HandleEvent(n);
            }
            else if(0==n)
            {
                Log(DEBUG,"time out\n");
                continue;
            }
            else
            {
                Log(ERROR,"epoll_wait error\n");
                break;
            }
        }
    }
private:
    int _listenSocket;
    int _port;
    int _epollFd;

    struct epoll_event _ready[defaultMaxEvent];
};